package util

import (
	"fmt"
	"gitee.com/kingzyt/common/log"
	"time"
)

const New_day_checkpoint_hour = 3

func GetTimeSubS(st int64, et int64) int32 {
	return int32((et - st) / int64(time.Second))
}
func GetTimeSubMS(st int64, et int64) int64 {
	return (et - st) / int64(time.Millisecond)
}
func GetTimeS(t time.Time) float32 {
	return float32(t.Unix())
}

func TimeStringYM(t time.Time) string {
	return t.Format("200601")
}
func TimeStringYMD(t time.Time) string {
	return t.Format("20060102")
}
func TimeStringYMD2(t time.Time) string {
	return t.Format("2006-01-02")
}
func TimeStringYMD3(t time.Time) string {
	return t.Format("2006/01/02")
}
func TimeStringYMD4(t time.Time) string {
	return t.Format("2006年01月02日")
}
func TimeStringYMDhm(t time.Time) string {
	return t.Format("2006年01月02日 15:04")
}
func TimeStringYMDhm2(t time.Time) string {
	return t.Format("2006/01/02 15:04")
}
func TimeStringYMDhm3(t time.Time) string {
	return t.Format("2006-01-02T15:04")
}
func TimeStringYMDhms(t time.Time) string {
	return t.Format("2006/01/02 15:04:05")
}
func TimeStringYMDhmInFileName(t time.Time) string {
	return t.Format("2006年01月02日 15_04")
}

func TimeYMDInt(t time.Time) int32 {
	y, m, d := t.Date()
	return int32(y*10000 + int(m)*100 + d)
}

func YMDToTime(ymd string, zero bool) (time.Time, error) {
	if zero {
		return time.Parse("20060102 15:04:05", ymd+" 00:00:00")
	} else {
		return time.Parse("20060102 15:04:05", ymd+" 23:59:59")
	}
}
func YMD2ToTime(ymd string, zero bool) (time.Time, error) {
	if zero {
		return time.Parse("2006-01-02 15:04:05", ymd+" 00:00:00")
	} else {
		return time.Parse("2006-01-02 15:04:05", ymd+" 23:59:59")
	}
}

func GetWeekDayRange(now time.Time) (beginT, endT time.Time) {
	wd := int(now.Weekday())
	if wd == 0 {
		wd = 7
	}
	monday := now.AddDate(0, 0, -(wd - 1))
	beginT = GetTargetDayZeroClockTime(monday)
	sunday := now.AddDate(0, 0, 7-wd)
	endT = GetTargetDayZeroClockTime(sunday).Add(time.Hour*24 - time.Second)
	return
}
func GetMonthDayRange(now time.Time) (beginT, endT time.Time) {
	y, m := now.Year(), now.Month()
	beginT = time.Date(y, m, 1, 0, 0, 0, 0, now.Location())
	if m == time.December {
		endT = time.Date(y+1, time.January, 1, 0, 0, 0, 0, now.Location()).Add(-time.Second)
	} else {
		endT = time.Date(y, m+1, 1, 0, 0, 0, 0, now.Location()).Add(-time.Second)
	}
	return
}
func GetTargetDayZeroClockTime(targetDay time.Time) time.Time {
	year, month, day := targetDay.Date()
	return time.Date(year, month, day, 0, 0, 0, 0, targetDay.Location())
}

func GetNextDayZeroClockTimestamp() int64 {
	now := time.Now()
	year, month, day := now.Date()
	return time.Date(year, month, day, 0, 0, 0, 0, now.Location()).AddDate(0, 0, 1).UnixNano()
}
func GetNextMonthZeroClockTimestamp() int64 {
	now := time.Now()
	year, month, _ := now.Date()
	if month == time.December {
		return time.Date(year+1, time.January, 1, 0, 0, 0, 0, now.Location()).UnixNano()
	} else {
		return time.Date(year, month+1, 1, 0, 0, 0, 0, now.Location()).UnixNano()
	}
}
func GetNextYearZeroClockTimestamp() int64 {
	now := time.Now()
	year, _, _ := now.Date()
	return time.Date(year+1, time.January, 1, 0, 0, 0, 0, now.Location()).UnixNano()
}

func GetCheckpointT(nextCheckPointT int64, now time.Time) (int64, bool) {
	if nextCheckPointT == 0 || nextCheckPointT < now.UnixNano() {
		year, month, day := now.Date()
		todayCPT := time.Date(year, month, day, New_day_checkpoint_hour, 0, 0, 0, now.Location())
		if now.Hour() >= New_day_checkpoint_hour {
			return todayCPT.AddDate(0, 0, 1).UnixNano(), true
		} else {
			return todayCPT.UnixNano(), true
		}
	}
	return nextCheckPointT, false
}

func GetCheckpointTByWeek(nextCheckPointT int64, now time.Time, weekDay time.Weekday) (int64, bool) {
	if nextCheckPointT == 0 || nextCheckPointT < now.UnixNano() {
		year, month, day := now.Date()
		todayCPT := time.Date(year, month, day, New_day_checkpoint_hour, 0, 0, 0, now.Location())
		nowWeekDay := now.Weekday()
		if nowWeekDay == weekDay {
			if now.Hour() >= New_day_checkpoint_hour {
				return todayCPT.AddDate(0, 0, 7).UnixNano(), true
			} else {
				return todayCPT.UnixNano(), true
			}
		} else {
			if weekDay > nowWeekDay {
				return todayCPT.AddDate(0, 0, int(weekDay-nowWeekDay)).UnixNano(), true
			} else {
				return todayCPT.AddDate(0, 0, int(7-(nowWeekDay-weekDay))).UnixNano(), true
			}
		}
	}
	return nextCheckPointT, false
}

// need that hourPoints is small to big seq, and range is [0,23]
func GetCheckpointTByCplist(nextCheckPointT int64, now time.Time, hourPoints []int32) (int64, bool) {
	if nextCheckPointT == 0 || nextCheckPointT < now.UnixNano() {
		year, month, day := now.Date()

		curHour := int32(now.Hour())
		nearestCPHour := int32(-1)
		for i := 0; i < len(hourPoints); i++ {
			hourPoint := hourPoints[i]
			if hourPoint < 0 || hourPoint > 23 {
				panic(fmt.Sprintf("hourPoint(%d) is out of range [0, 23]", hourPoint))
			}
			if curHour < hourPoint {
				nearestCPHour = hourPoint
				break
			}
		}
		if nearestCPHour < 0 {
			return time.Date(year, month, day, int(hourPoints[0]), 0, 0, 0, now.Location()).AddDate(0, 0, 1).UnixNano(), true
		} else {
			return time.Date(year, month, day, int(nearestCPHour), 0, 0, 0, now.Location()).UnixNano(), true
		}
	}
	return nextCheckPointT, false
}

func TimeToString(t time.Time) string {
	return t.Format(time.RFC3339)
}
func TimestampToString(t int64) string {
	return time.Unix(t/1000000000, t%1000000000).Format(time.RFC3339)
}
func TimestampToTime(t int64) time.Time {
	return time.Unix(t/1000000000, t%1000000000)
}
func StringToTime(layout, t string) (tt time.Time, err error) {
	if t == "" {
		err = fmt.Errorf("string is empty, not time")
		log.Error("", err.Error())
		return
	}

	tt, err = time.Parse(layout, t)
	if err != nil {
		err = fmt.Errorf("t:%s to time.Time fail, err:%s", t, err)
		log.Error("", err.Error())
		return
	}
	return
}
func StringToTime2(t string) (tt time.Time, err error) {
	return StringToTime(time.RFC3339, t)
}
func StringToTimestamp(t string) (timestamp int64, err error) {
	return StringToTimestampCore(time.RFC3339, t)
}
func StringToTimestampCore(layout, t string) (timestamp int64, err error) {
	tt, err := StringToTime(layout, t)
	if err != nil {
		return 0, err
	}
	return tt.UnixNano(), nil
}

func IsInTime(beginTime string, endTime string, now int64) bool {
	b, err := StringToTimestamp(beginTime)
	if err != nil {
		return false
	}

	if now < b {
		return false
	}

	e, err := StringToTimestamp(endTime)
	if err != nil {
		return false
	}
	if now > e {
		return false
	}

	return true
}

func GetBeginEndTWithNewDayHour(now time.Time) (beginT time.Time, endT time.Time) {
	h := now.Hour()
	if h < New_day_checkpoint_hour {
		beginT = GetTargetDayZeroClockTime(now).AddDate(0, 0, -1).Add(time.Hour * New_day_checkpoint_hour)
	} else {
		beginT = GetTargetDayZeroClockTime(now).Add(time.Hour * New_day_checkpoint_hour)
	}
	endT = beginT.AddDate(0, 0, 1)
	return
}

func LoopYMD(beginDay, endDay time.Time, proc func(y, m, d int) bool) {
	ymd := GetTargetDayZeroClockTime(beginDay)
	ymdCur := TimeYMDInt(ymd)
	ymdEnd := TimeYMDInt(endDay)
	for ymdCur <= ymdEnd {
		y, m, d := ymd.Date()
		if !proc(y, int(m), d) {
			return
		}
		ymd = ymd.AddDate(0, 0, 1)
		ymdCur = TimeYMDInt(ymd)
	}
}

func LoopMonth(beginDay, endDay time.Time, proc func(y, m int, begint, endt time.Time) bool) {
	ymd := GetTargetDayZeroClockTime(beginDay)
	ymdCur := TimeYMDInt(ymd)
	ymdEnd := TimeYMDInt(endDay)
	curM := 0
	for ymdCur <= ymdEnd {
		y, m, _ := ymd.Date()
		if curM != int(m) {
			bt, et := GetMonthDayRange(time.Date(y, m, 1, 0, 0, 0, 0, beginDay.Location()))
			if !proc(y, int(m), bt, et) {
				return
			}
			curM = int(m)
		}
		ymd = ymd.AddDate(0, 0, 1)
		ymdCur = TimeYMDInt(ymd)
	}
}
func LoopWeek(beginDay, endDay time.Time, proc func(begint, endt time.Time) bool) {
	curDay := beginDay
	for curDay.UnixNano() < endDay.UnixNano() {
		bt, et := GetWeekDayRange(curDay)
		if !proc(bt, et) {
			return
		}
		curDay = curDay.AddDate(0, 0, 7)
	}
}

func GetPeriodYMDs(beginT, endT time.Time) (ymds []string) {
	bt := GetTargetDayZeroClockTime(beginT)
	et := GetTargetDayZeroClockTime(endT)
	t := bt
	for t.Unix() <= et.Unix() {
		ymds = append(ymds, TimeStringYMD(t))
		t = t.AddDate(0, 0, 1)
	}
	return
}

// src value is 2021-02-22T07:15, so add ':00'
func HtmlDatetimeToDatetime(htmldatetime string) (time.Time, error) {
	return StringToTime2(htmldatetime + ":00+08:00")
}
func DatetimeToHtmlDatetime(dt time.Time) string {
	return TimeToString(dt)[:16]
}

// src value is 2021-02-22
func HtmlDateToDatetimeStr(htmldate string) string {
	return htmldate + "T00:00:00+08:00"
}
func HtmlDateToDatetime(htmldate string) (time.Time, error) {
	return StringToTime2(HtmlDateToDatetimeStr(htmldate))
}
func DatetimeToHtmlDate(dt time.Time) string {
	return TimeToString(dt)[:10]
}

var bt = time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local)

func ExcelDateIntToDate(dateInt string) (t time.Time, err error) {
	var tint float32
	_, err = fmt.Sscanf(dateInt, "%f", &tint)
	if err != nil {
		return
	}
	return bt.AddDate(0, 0, int(tint)), nil
}
func ExcelDateToDate(date string) (t time.Time, err error) {
	return time.Parse("01-02-06T15:04:05Z07:00", date+"T00:00:00+08:00")
}
func DateToExcelDate(t time.Time) (date string) {
	return t.Format("01-02-06")
}
